// I_cyber.c
// $Revision: 538 $
// $Date: 2009-12-05 14:40:08 +0200 (Sat, 05 Dec 2009) $

#include <dos.h>
#include <stdlib.h>


/*
====================================================

Doom control structure

The keybaord and joystick will add to the values set by the cyberman,
to a maximum of 0x19000 for forwardmove and sidemove.  Angleturn is
not bounded at all.

parm            normal          fast
-----           ------          ----
forwardmove     0xc800          0x19000
sidemove        0xc000          0x14000
angleturn       0x2800000       0x5000000

The keyboard and joystick have a 1/3 second slow turn of 0x1400000 under
normal speed to help aiming.

====================================================
*/

typedef struct
{
	signed char	forwardmove;	// *2048 for move
	signed char	sidemove;	// *2048 for move
	short		angleturn;	// <<16 for angle delta
	short		consistancy;	// checks for net game
	unsigned char	chatchar;
	unsigned char	buttons;
} ticcmd_t;

#define BT_ATTACK		1
#define BT_USE			2
#define BT_CHANGE		4	// if true, the next 3 bits hold weapon num
#define BT_WEAPONMASK	(8+16+32)
#define BT_WEAPONSHIFT	3

//==================================================
//
// CyberMan detection and usage info
//
//==================================================

#define DPMI_INT	0x31
#define MOUSE_INT	0x33

#define DOSMEMSIZE	64	// enough for any SWIFT structure

typedef struct {
   short	x;
   short	y;
   short	z;
   short	pitch;
   short	roll;
   short	yaw;
   short	buttons;
} SWIFT_3DStatus;

// DPMI real mode interrupt structure
static struct rminfo {
	long	EDI;
	long	ESI;
	long	EBP;
	long	reserved_by_system;
	long	EBX;
	long	EDX;
	long	ECX;
	long	EAX;
	short	flags;
	short	ES,DS,FS,GS,IP,CS,SP,SS;
} RMI;

typedef struct {
   unsigned char	deviceType;
   unsigned char	majorVersion;
   unsigned char	minorVersion;
   unsigned char	absRelFlags;
   unsigned char	centeringFlags;
   unsigned char	reserved[5];
} StaticDeviceData;

// values for deviceType:
#define DEVTYPE_CYBERMAN	1

short		selector;
unsigned short	segment;		// segment of DOS memory block
SWIFT_3DStatus	*cyberstat;
int		isCyberPresent;		// is CyberMan present?

static union  REGS	regs;
static struct SREGS	sregs;

extern int	mousepresent;


//===========================================================
//
// I_StartupCyberMan
//
// If a cyberman is present, init it and set isCyberPresent to 1
//===========================================================

void I_StartupCyberMan(void)
{
	StaticDeviceData *pbuf;

	isCyberPresent = 0;

	cyberstat = (SWIFT_3DStatus *)I_AllocLow (DOSMEMSIZE);
	segment = (int)cyberstat>>4;

	pbuf = (StaticDeviceData *)cyberstat;
	memset(pbuf, 0, sizeof (StaticDeviceData));

	// Use DPMI call 300h to issue mouse interrupt
	memset(&RMI, 0, sizeof(RMI));
	RMI.EAX = 0x53C1;		// SWIFT: Get Static Device Data
	RMI.ES = segment;
	RMI.EDX = 0;
	memset(&sregs, 0, sizeof (sregs));
	regs.w.ax = 0x0300;		// DPMI: simulate interrupt
	regs.w.bx = MOUSE_INT;
	regs.w.cx = 0;
	regs.x.edi = FP_OFF(&RMI);
	sregs.es = FP_SEG(&RMI);
	int386x( DPMI_INT, &regs, &regs, &sregs );

	if ((short)RMI.EAX != 1)
	{
		// SWIFT functions not present
		tprintf("CyberMan: Wrong mouse driver - no SWIFT support (AX=%04x).\n",
							(unsigned)(short)RMI.EAX);
	}
	else if (pbuf->deviceType != DEVTYPE_CYBERMAN)
	{
		// no SWIFT device, or not CyberMan
		if (pbuf->deviceType == 0)
		{
			tprintf("CyberMan: no SWIFT device connected.\n");
		}
		else
		{
			tprintf("CyberMan: SWIFT device is not a CyberMan! (type=%d)\n",
								pbuf->deviceType);
		}
	}
	else
	{
		tprintf("CyberMan: CyberMan %d.%02d connected.\n",
				pbuf->majorVersion, pbuf->minorVersion);
		isCyberPresent = 1;
		mousepresent = 0;
	}
}


/*
===============
=
= I_ReadCyberCmds
=
===============
*/

int		oldpos;

void I_ReadCyberCmd (ticcmd_t *cmd)
{
	int	delta;

	// Use DPMI call 300h to issue mouse interrupt
	memset(&RMI, 0, sizeof(RMI));
	RMI.EAX = 0x5301;		// SWIFT: Get Position and Buttons
	RMI.ES = segment;
	RMI.EDX = 0;
	memset(&sregs, 0, sizeof (sregs));
	regs.w.ax = 0x0300;		// DPMI: simulate interrupt
	regs.w.bx = MOUSE_INT;
	regs.w.cx = 0;
	regs.x.edi = FP_OFF(&RMI);
	sregs.es = FP_SEG(&RMI);
	int386x( DPMI_INT, &regs, &regs, &sregs );

	if (cyberstat->y < -7900)
		cmd->forwardmove = 0xc800/2048;
	else if (cyberstat->y > 7900)
		cmd->forwardmove = -0xc800/2048;

	if (cyberstat->buttons & 4)
		cmd->buttons |= BT_ATTACK;
	if (cyberstat->buttons & 2)
		cmd->buttons |= BT_USE;

	delta = cyberstat->x - oldpos;
	oldpos = cyberstat->x;

	if (cyberstat->buttons & 1)
	{	// strafe
		if (cyberstat->x < -7900)
			cmd->sidemove = -0xc800/2048;
		else if (cyberstat->x > 7900)
			cmd->sidemove = 0xc800/2048;
		else
			cmd->sidemove = delta*40/2048;
	}
	else
	{
		if (cyberstat->x < -7900)
			cmd->angleturn = 0x280;
		else if (cyberstat->x > 7900)
			cmd->angleturn = -0x280;
		else
			cmd->angleturn = -delta*0xa/16;
	}
}


void I_Tactile (int on, int off, int total)
{
	if (!isCyberPresent)
		return;

	on /= 5;
	off /= 5;
	total /= 40;
	if (on > 255)
		on = 255;
	if (off > 255)
		off = 255;
	if (total > 255)
		total = 255;

	memset(&RMI, 0, sizeof(RMI));
	RMI.EAX = 0x5330;		// SWIFT: Get Position and Buttons
	RMI.EBX = on*256+off;
	RMI.ECX = total;
	memset(&sregs, 0, sizeof (sregs));
	regs.w.ax = 0x0300;		// DPMI: simulate interrupt
	regs.w.bx = MOUSE_INT;
	regs.w.cx = 0;
	regs.x.edi = FP_OFF(&RMI);
	sregs.es = FP_SEG(&RMI);
	int386x( DPMI_INT, &regs, &regs, &sregs );
}

